﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>

void _1_或和与运算的短路现象() {
	int a = 1, b = 2;
	int k = ++a || b-- + 1 % 2 / 3 - 4 * 5 >= 0 || !0 != 1 && ++a + 4;
	//考虑 a=? b=? k=?
	printf("a=%d,b=%d,k=%d\n", a, b, k);//a=2,b=2,k=1
	//由于++a为非0右边是||,故无论||的右边是0还是非0结果都是确定的，一真则真，于是右边所有的表达式都被短路了！！
	//<非零> || <表达式>始终为1, 且<表达式>不执行

	//类似的&&也有这个现象
	//<零> && <表达式>始终为0,且<表达式>不执行
	a = 0, b = 1;
	k = a && b++ || 5;
	printf("a=%d,b=%d,k=%d\n", a, b, k);//a=0,b=1,k=1
}

void _2_冒泡排序算法() {
	int a[10];
	printf("input 10 numbers:\n");
	for (int i = 0; i < 10; i++) {
		scanf("%d", &a[i]);
	}
	puts("升序冒泡排序:");
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9 - i; j++) {
			if (a[j] > a[j + 1]) {
				a[j] = a[j] + a[j + 1];
				a[j + 1] = a[j] - a[j + 1];
				a[j] = a[j] - a[j + 1];
			}
		}
	}
	for (int i = 0; i <= 9; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

void _3_选择排序算法() {
	int a[11];
	printf("请输入十个正整数:\n");
	for (int i = 1; i <= 10; i++) {
		scanf("%d", &a[i]);
	}
	//升序选择排序
	for (int i = 1; i <= 9; i++) {
		int min = i;
		for (int j = i + 1; j <= 10; j++) {
			if (a[j] < a[min]) {
				//先不急着交换，只把下标记下来，最后遍历完了，换一次就行
				min = j;
			}
		}
		//与最小项交换
		if (min != i) {//这里如果没有判断，当i就是本轮中最小的下标时，a[i]与a[min]是同一项，下面这种交换方式就出错了，但经典两数借助temp交换就不会出错
			a[i] = a[i] + a[min];
			a[min] = a[i] - a[min];
			a[i] = a[i] - a[min];
		}
		/*int temp = a[i];
		a[i] = a[min];
		a[min] = temp;*/
	}
	printf("升序选择排序:\n");
	for (int i = 1; i <= 10; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

void _4_有序数组插入一个数_正序后移() {
	int a[11] = { 1,4,6,9,13,16,19,28,40,100 };
	printf("原数组:\n");
	for (int i = 0; i <= 9; i++) {
		printf("%4d", a[i]);
	}
	int num;
	printf("\n请输入num的值:");
	scanf("%d", &num);

	if (num >= a[9])//大于最后一项直接放最后即可
		a[10] = num;
	else//否则需要依次往后移动项
		for (int i = 0; i <= 9; i++) {
			if (a[i] > num) {
				//说明num应该在a[i]前面，这时候要将a[i]到a[9]全部后移一位

				////法一：倒序移动，最简单容易想到，不存在覆盖的问题
				//for (int j = 9; j >= i; j--) {
				//	a[j + 1] = a[j];
				//}

				////法二：正序移动，如何解决覆盖问题？直线前进型
				//int last, next;//last存放的是正在移动的那一项的值，next存放的是要移动到的那个位置原来里面的值
				//last = a[i];
				//for (int j = i; j <= 9; j++) {
				//	next = a[j + 1];//让出原来下一项的位置
				//	a[j + 1] = last;//把当前项填充到空出来的位置
				//	last = next;//把原来的下一项作为当前项
				//}

				////法三：法二改，正序移动，不借助额外变量，使用a[i]和a[10]
				//for (int j = i; j <= 9; j++) {
				//	a[10] = a[j + 1];//a[10]存放被覆盖项
				//	a[j + 1] = a[i];//a[i]存放待移动项
				//	a[i] = a[10];//把下一项变成带移动项
				//}

				////法四：正序移动，两侧摇摆型，相比法二容易想到，符合人类思维
				//int t1, t2/*,flag=0*/;
				//t1 = a[i];
				//for (int j = i; j <= 9; j++) {
				//	if ((j - i) % 2 == 0/*flag==0*/) {//用奇偶性可以省去标志位
				//		t2 = a[j + 1];
				//		a[j + 1] = t1;
				//		/*flag=1;*/
				//	}
				//	else {
				//		t1 = a[j + 1];
				//		a[j + 1] = t2;
				//		/*flag=0;*/
				//	}
				//}

				////法五：法四改，不用t1,t2直接用a[i]和a[10]
				//for (int j = i; j <= 9; j++) {
				//	if ((j - i) % 2 == 0) {
				//		a[10] = a[j + 1];
				//		a[j + 1] = a[i];
				//	}
				//	else {
				//		a[i] = a[j + 1];
				//		a[j + 1] = a[10];
				//	}
				//}

				//法六：交换型，可以借助一个额外变量或者不借助
				/*int temp;*/
				for (int j = i + 1; j <= 10; j++) {
					a[j] = a[i] + a[j];
					a[i] = a[j] - a[i];
					a[j] = a[j] - a[i];
					/*temp = a[i];
					a[i] = a[j];
					a[j] = temp;*/
				}

				a[i] = num;
				break;
			}
		}

	printf("插入%d后的数组:\n", num);
	for (int i = 0; i <= 10; i++) {
		printf("%4d", a[i]);
	}
	printf("\n");
}


int sum = 0;
void _5_汉诺塔() {
	void hanoi(int n, char one, char two, char three);//对hanoi函数的声明
	int m;
	printf("input the number of disks: ");
	scanf("%d", &m);
	printf("The steps to move %d disks:\n", m);
	hanoi(m, 'A', 'B', 'C');//表示要从A塔移动m个盘到C塔，借助B塔
	printf("共移动了%d次\n", sum);
	return 0;
}
void hanoi(int n, char one, char two, char three) {
	//将n个盘从one塔移动到three塔，借助two塔
	void move(char x, char y);//对move函数的声明
	if (n == 1) {
		move(one, three);//表示将一个圆盘从one塔移动到three塔
	}
	else {
		hanoi(n - 1, one, three, two);//先将one上的n-1个借助three移动到two
		move(one, three);//将one上剩余的1个直接移动到three上
		hanoi(n - 1, two, one, three);//将刚才从one上移动到two上的n-1个借助one移动到three
	}
}
void move(char x, char y) {
	printf("%c-->%c\n", x, y);
	sum++;
}



void _6_i加加和加加i运算顺序问题以及函数参数的运算顺序() {
	//思考:i++什么时候+1,是语句结束了+1,还是i++参与运算后就立刻+1?
	int i = 2;
	int k = (i++) + i;//这里输出k=4,i=3,Visual Studio的逻辑是把++i变成i=i+1放到本语句前面,有几个++i i的值就加几,用i的最终值参与运算;i++就用所有++i加完的值作为它的值参与运算,然后在本语句结束后进行i=i+1有几个i++ i的值就加几
	printf("k=%d,i=%d\n", k, i);//k=4,i=3	
	//注意:gcc下输出"k=5,i=3",考试应该以5为准,说明i++是用当前旧值2参与运算,同时就把自身+1变成新值3,所以第二个i是3,k=2+3=5,i自身等于3

	i = 2;
	k = (i++) + (++i);
	printf("k=%d,i=%d\n", k, i);//k=6,i=4
	//gcc输出k=6,i=4,虽然二者结果一样但是逻辑同,以DevC++为准,i++用2参与运算,i自身已经变成3,++i结果是4,i自身是4,所以是k=2+4=6;VS是先i=i+1;i已经变成3,然后k=3+3=6,再i=i+1;i变成4

	i = 3; k = (++i) + (++i) + (i++);//k=5+5+5
	printf("k=%d,i=%d\n", k, i);//k=15,i=6
	i = 3; k = (++i) + (++i) + (++i);//k=6+6+6
	printf("k=%d,i=%d\n", k, i);//k=18,i=6
	i = 3; k = (i++) + (i++) + (i++);//k=3+3+3
	printf("k=%d,i=%d\n", k, i);//k=9,i=6
	//gcc输出
	/*
	k=14,i=6 即k=4+5+5=14
	k=15,i=6 即k=4+5+6=15
	k=12,i=6 即k=3+4+5=12
	*/
	/*总结:以gcc为准
	i++是用旧值参与运算,同时自身就+1而不是等到语句结束再+1,后面的运算使用新值;++i就是直接自身+1,同时使用新值参加运算
	另外,两者都不能作为左值
	*/

	i = 2;
	printf("%d,%d\n", i++, i++);//3,2 从右往左运算确定实参的值后再调用函数
	i = 2;
	printf("%d,%d\n", ++i, i++);//4,2
	i = 2;
	printf("%d,%d,%d\n", i, i++, i++);//4,3,2
	i = 2;
	printf("%d,%d,%d\n", i++, i, i++);//gcc输出3,3,2 
	i = 2;
	printf("%d,%d,%d\n", ++i, i, i++);//gcc输出4,3,2
	i = 2;
	printf("%d,%d\n", i++, ++i);//gcc输出3,3
	i = 2;
	printf("%d,%d,%d,%d\n", i++, ++i, i, i++);//gcc输出4,4,3,2
	printf("%d\n", i);//5
	/////////////////////////////////////////
	int j = 7;
	printf("%d\n", j++ * j++);//gcc输出56  7*8=56 从右向左定实参的值,这里只有一个实参,但是内部乘法运算是自左向右结合的,所以左边的j++是7,右边的是8;一定要记住j++用一次之后就立刻加一
	printf("%d\n", j);//9
	/////////////////////////////////////////
	int a = 5, b = 0;
	printf("%d %d\n", b = a + 1, a = a + 1);//7 6
	/////////////////////////////////////////
	void test(int, int, int);
	i = 2;
	test(i++, ++i, i++);//gcc输出a=4,b=4,c=2 说明不仅仅是printf函数,所有函数在调用时,都是从右往左运算确定实参的值后再形实结合调用函数的
	printf("i=%d\n", i);//i=5
	/////////////////////////////////////////
	int arr[] = {
		6, 7, 8, 9, 10
	};
	int* ptr = arr; //ptr指向6
	*(ptr++) += 123;//等价于*ptr = *(ptr++) + 123;
	//*(ptr++) += 123;并不等价于*(ptr++) = *(ptr++) + 123;它的结果是*ptr=8 arr[0]=130
	// 也不等价于*(ptr++) = *(ptr) + 123;它的结果是*ptr=7 arr[0]=130
	// 另外*(++ptr) += 123;等价于*(++ptr) = *ptr+123;它的结果是*ptr=130 arr[0]=6
	printf("*ptr=%d\n", *ptr); //*ptr=7 此时ptr指向7
	printf("arr[0]=%d\n", arr[0]); //arr[0]=129
	printf("%d,%d\n", *ptr, *(++ptr));//8,8
	//符合赋值运算符a+=b等于a=a+b,是把a照抄到等号后面,但是当和++--连用时,情况有些复杂了,++--会改变a的值;前++--是先自增然后参与运算,于是照抄到后面的应该是自增过后的值,而不是整个带++--的a,++--不会被照抄过去,即不会执行两次++--,代码中出现几次就算几次,这里出现了一次,于是只会自增自减一次;后++--是先用旧值参与运算然后立刻自增自减,同样也是把参与运算的值照抄到后面,即后面这个a不会因为前面的a自增自减后而改变,它依旧是原来旧的参与运算的值,本题中参与运算的就是*ptr,于是尽管ptr立刻自增1指向了7,但是后面照抄的依旧是与前面参与运算的实际值相同的*ptr,它指向的是原来的6,所以arr[0]=6+123=129,ptr却指向了7
	/////////////////////////////////////////
	i = 5;
	i = i++ + 5;
	printf("i=%d\n", i);//gcc输出i=10,因为i++在用5参与运算的同时自身变成了6,但是i=5+5导致i变成了10,并不是先i=5+5结束了再i++的;VS中结果是11,显然是先参与运算后单独加一的,以gcc为准
}
void test(int a, int b, int c) {
	printf("a=%d,b=%d,c=%d\n", a, b, c);
}

void _7_最大公约数和最小公倍数() {
	int m, n, p, r;
	printf("请输入两个正整数n,m:");
	scanf("%d%d", &m, &n);
	p = m * n;//保存一开始m*n的值，一会儿用来计算最小公倍数
	printf("%d和%d的", m, n);
	/*
	辗转相除法计算最大公约数

	被除数与除数的最大公约数就是除数和余数的最大公约数，于是不断把除数变成被除数，余数变成除数，直到除数为0的时候，被除数就是最大公约数（因为0与任何数的最大公约数就是那个数本身）*/

	while (n) {//默认m为被除数，n为除数。当除数n!=0的时候
		r = m % n;
		m = n;//把除数赋给被除数
		n = r;//把余数赋给除数
	}
	printf("最大公约数是%d,", m);//出循环时除数为0，被除数就是最大公约数
	printf("最小公倍数是%d\n", p / m);//最小公倍数 = m*n / 最大公约数，因为m*n/公因子c=(m/c)*n=a*n=m*(n/c)=m*b，所以结果一定还是n和m的公倍数，于是当公因子c最大时，就是m和n的最小公倍数，即c为最大公约数
}

void _8_回文串() {
	char str[20], temp;
	scanf("%s", str);
	for (int i = 0, j = strlen(str) - 1; i < j/*或i<strlen(str)/2*/; i++, j--) {
		temp = str[i];
		str[i] = str[j];
		str[j] = temp;
	}
	printf("%s\n", str);
}

void _9_折半查找法() {
	int a[15], i;
	printf("enter data:\n");
	scanf("%d", &a[0]);
	i = 1;
	while (i < 15) {
		scanf("%d", &a[i]);
		if (a[i] >= a[i - 1])
			i++;
		else
			printf("enter this data again:\n");
	}
	printf("\n");
	printf("15个数是:");
	for (i = 0; i < 15; i++) {
		printf("%5d", a[i]);
	}
	printf("\n");
	int flag = 1, number, sign, top, bott, loca = 0, mid;
	while (flag) {
		printf("input number to look for:");
		scanf("%d", &number);
		sign = 0;
		top = 0;
		bott = 15 - 1;
		if (number<a[0] || number>a[14]) {
			loca = -1;
		}
		while (!sign && top <= bott) {
			mid = (bott + top) / 2;
			if (number == a[mid]) {
				loca = mid;
				printf("Has found %d ,its position is %d\n", number, loca + 1);
				sign = 1;
			}
			else
				if (number < a[mid]) {
					bott = mid - 1;
				}
				else {
					top = mid + 1;
				}
		}
		if (!sign || loca == -1) {
			printf("cannot find %d\n", number);
		}
		printf("continue or not(Y/N)?");
		char c;
		getchar();
		scanf("%c", &c);
		if (c == 'N' || c == 'n')
			flag = 0;
	}
}

void _10_进制转换() {
	//十六/八/二/十进制字符数组转十进制整数
	char s[10] = "A1";
	int result = 0;
	for (int i = 0; i < strlen(s); i++) {
		if (s[i] >= '0' && s[i] <= '9') {
			result = result * 16 + s[i] - '0';
			//<数字字符数组转换成十进制整数>
			//int r=0; 
			//for(字符数组从头到尾正序遍历){
			//	r=r*进制数+本位数值;
			// }
			//进制数可以是2 8 16 10(任何)
			//原理是二进制/八进制/十六进制转十进制用的"不断除进制数,倒序输出余数"法的逆运算
		}
		else if (s[i] >= 'A' && s[i] <= 'Z') {
			result = result * 16 + s[i] - 'A' + 10;
		}
		else if (s[i] >= 'a' && s[i] <= 'z') {
			result = result * 16 + s[i] - 'a' + 10;
		}
	}
	printf("result=%d\n", result);
	//十进制字符数组转二/八/十六进制字符数组
	char a[10] = "175", b[30];
	result = 0;
	for (int i = 0; a[i] != '\0'; i++) {
		result = result * 10 + a[i] - '0';//char数组转int
	}
	printf("十进制整数是%d\n", result);
	//"不断除进制数,倒序输出余数"法,当除的进制数为十时,实际上就是把int转成了数字字符数组,只不过依然是十进制的
	int shang = -1, yushu = -1, i = 0;
	do {
		shang = result / 16;
		yushu = result % 16;
		if (yushu < 10)
			b[i++] = yushu + '0';
		else
			b[i++] = yushu - 10 + 'A';
		result = shang;
	} while (shang != 0);
	printf("转换结果是");
	for (i--; i >= 0; i--) {
		printf("%c", b[i]);
	}
}

void _11_递归法将int转成数字char数组() {
	int num;
	scanf("%d", &num);
	char result[20] = "";
	int convert(int, char[]);
	int temp = convert(num, result);
	printf("temp=%d\n", temp);
	for (int i = 0; i <= temp; i++) {
		printf("%c ", result[i]);
	}
}
//是正序放入数组的,而不是通常的不断%10那样是倒着放的
int convert(int num, char result[]) {
	static int i = 0;
	int shang = num / 10;
	if (shang != 0) {
		convert(shang, result);
	}
	result[i++] = num % 10 + '0';
	return i - 1;
}


void sort(char* name[], int n) {
	for (int i = 0; i <= n - 2; i++) {
		int k = i;
		for (int j = i + 1; j <= n - 1; j++) {
			if (strcmp(name[k], name[j]) > 0) {
				k = j;
			}
		}
		if (k != i) {
			int* temp = *(name + k);
			name[k] = name[i];
			name[i] = temp;
		}
	}
}

void print(char** name, int n) {
	for (int i = 0; i <= n - 1; i++) {
		//printf("%s\n",  name[i]);
		puts(name[i]);
	}
}

void _12_对用字符指针数组表达的字符串数组按字母顺序进行选择排序() {
	char* name[] = { "Follow me","BASIC","Great Wall","FORTRAN","Computer design" };
	sort(name, 5);
	print(name, 5);
}



#define LEN sizeof(struct Student)
struct Student {
	long num;
	float score;
	struct Student* next;
};
int n;
//动态建立链表
struct Student* createLinkedList(void) {
	struct Student* head;
	struct Student* p1, * p2;
	n = 0;
	p1 = p2 = (struct Student*)malloc(LEN);
	scanf("%ld,%f", &p1->num, &p1->score);
	head = NULL;
	while (p1->num != 0) {
		n++;
		if (n == 1) {
			head = p1;
		}
		else {
			p2->next = p1;
		}
		p2 = p1;
		p1 = malloc(LEN);
		scanf("%ld,%f", &p1->num, &p1->score);
	}
	p2->next = NULL;
	return head;
}
//输出链表
void printLinkedList(struct Student* head) {
	printf("链表内容如下,共有%d个结点:\n", n);
	while (head != NULL) {
		printf("%ld %5.1f\n", head->num, head->score);
		head = head->next;
	}
}

void _13_动态建立链表() {
	/*struct Student stu = { .score = 5 ,.num = 7 };
	printf("%d,%f\n", stu.num, stu.score);*/
	struct Student* head;
	head = createLinkedList();
	printLinkedList(head);
}

void _14_使用fgetc和fputc写文件a并复制到b() {
	//写一个新文件
	char filename[20];
	printf("请输入新文件名:");
	scanf("%s", filename);
	FILE* writeData = fopen(filename, "w");
	char ch;
	printf("请输入文件\"");
	printf("%s", filename);
	printf("\"的内容:\n");
	getchar();//清除回车
	ch = getchar();
	while (ch != '@') {
		fputc(ch, writeData);
		ch = getchar();
	}
	fclose(writeData);
	putchar(10);

	//复制文件内容
	FILE* in, * out;
	char  infile[20], outfile[20];
	printf("请输入读入文件名:");
	scanf("%s", infile);
	printf("请输入输出文件名:");
	scanf("%s", outfile);
	if ((in = fopen(infile, "r")) == NULL) {
		printf("无法打开此文件\n");
		exit(0);
	}
	if ((out = fopen(outfile, "w")) == NULL) {
		printf("无法打开此文件\n");
		exit(0);
	}
	ch = fgetc(in);
	while (ch != EOF) {
		fputc(ch, out);
		putchar(ch);
		ch = fgetc(in);
	}
	putchar('\12');
	fclose(in);
	fclose(out);
}

void 	_15_从键盘读入若干个字符串_对他们按字母大小顺序排序_然后把排好序的字符串送到磁盘文件中保存_谭浩强书上版本() {
	char str[3][10], temp[10];
	int i, j, k, n = 3;
	printf("Enter 3 String:\n");
	for (i = 0; i < n; i++) {
		gets(str[i]);
	}

	//对字符串按字母顺序进行选择排序
	for (i = 0; i <= n - 2; i++) {
		k = i;
		for (j = i + 1; j <= n - 1; j++) {
			if (strcmp(str[k], str[j]) > 0) {
				k = j;
			}
		}
		if (k != i) {
			strcpy(temp, str[i]);
			strcpy(str[i], str[k]);
			strcpy(str[k], temp);
		}
	}

	FILE* fp;
	if ((fp = fopen("string.txt", "w")) == NULL) {
		printf("文件无法打开!");
		exit(0);
	}

	printf("\nThe new sequence is:\n");
	for (i = 0; i <= n - 1; i++) {
		fputs(str[i], fp);
		fputc('\n', fp);//因为'\0'不会写入文件,为了区分前后字符串,认为加'\n'
		printf("%s\n", str[i]);
	}
}


void _15_改_根据指定的字符串个数n_动态建立了字符指针数组_并动态申请空间让每一个字符指针都指向一维字符数组_相当于二维数组的功能_但排序时只需要改变字符数组指向即可_无需真的交换字符数组中的内容_排序写了冒泡和选择两种_并且用fputs输出到文件file1_再用fgets读入() {
	/*
	Brazil
	China
	India
	America
	Britain
	*/
	printf("请输入字符串个数:");
	int n;
	scanf("%d", &n);
	getchar();
	char** shuzu = (char**)malloc(n * sizeof(char*));
	printf("请输入%d个字符串(每个长度不超过20个字符):\n", n);
	for (int i = 0; i < n; i++) {
		shuzu[i] = (char*)malloc(21 * sizeof(char));
		gets(shuzu[i]);
	}
	puts("\n输入的字符串如下:");
	for (int i = 0; i < n; i++) {
		puts(shuzu[i]);
	}
	//冒泡排序(升序)
	for (int i = 1; i <= n - 1; i++) {
		for (int j = 0; j <= n - 1 - i; j++) {
			if (strcmp(shuzu[j], shuzu[j + 1]) > 0) {
				char* temp;
				temp = shuzu[j];
				shuzu[j] = shuzu[j + 1];
				shuzu[j + 1] = temp;
			}
		}
	}

	//选择排序(降序)
	/*for (int i = 1; i <= n - 1; i++) {
		int max = i - 1;
		for (int j = i; j <= n - 1; j++) {
			if (strcmp(shuzu[j], shuzu[i - 1]) > 0) {
				max = j;
			}
		}
		if (max != i - 1) {
			char* temp;
			temp = shuzu[i - 1];
			shuzu[i - 1] = shuzu[max];
			shuzu[max] = temp;
		}
	}*/

	puts("\n排序后的字符串如下:");
	for (int i = 0; i < n; i++) {
		puts(shuzu[i]);
	}

	FILE* fp = fopen("file1", "wb");
	for (int i = 0; i < n; i++) {
		fputs(shuzu[i], fp);
		fputc('\n', fp);
		printf("存储了字符串:%s\n", shuzu[i]);
	}
	fclose(fp);
	puts("");
	fp = fopen("file1", "rb");
	char temp[22];
	while (fgets(temp, 22, fp) != NULL) {
		printf("%s", temp);
	}
	fclose(fp);
}

_16_使用fprintf和fscanf输出输入数值到文件() {
	FILE* out = fopen("test.txt", "w");
	int i = 3;
	float j = 4.5;
	printf("将%d,%6.2f存入文件\n", i, j);
	fprintf(out, "%d,%6.2f", i, j);
	fclose(out);
	i = 1;
	j = 1.5;
	printf("修改i,j的值%d,%f\n", i, j);
	FILE* in = fopen("test.txt", "r");
	fscanf(in, "%d,%f", &i, &j);
	fclose(in);
	printf("从文件读入i,j为%d,%f", i, j);
}


#define SIZE 10
struct Student_type {
	char name[10];
	int num;
	int age;
	char addr[15];
};

void _17_从键盘输入10个学生的信息_并按照学号排序_再输出到二进制文件_最后从文件读取数据_并打印在屏幕上() {
	/*

Zhang 1001 19 room_101
Liu 1014 21 room_110
Sun 1002 20 room_102
Li 1006 22 room_105
Ling 1004 21 room_104
Zhen 1008 16 room_107
Wang 1007 20 room_106
Tan 1003 21 room_103
Qin 1012 19 room_109
Fu 1010 18 room_108

	*/

	struct Student_type stud[SIZE];
	printf("请输入10个学生的数据:\12");
	for (int i = 0; i <= SIZE - 1; i++) {
		scanf("%s%d%d%s", stud[i].name, &stud[i].num, &stud[i].age, stud[i].addr);
	}
	//按照学号降序排序(选择排序)
	void SelectSort(struct Student_type* stud);
	SelectSort(stud);
	//按照学号降序排序(冒泡排序)
	//void BubbleSort(struct Student_type* stud);
	//BubbleSort(stud);

	//输出排序后结果
	printf("排序后结果为:\n=================================\n");
	for (int i = 0; i <= SIZE - 1; i++) {
		printf("序号:%d:\n姓名:%s\n学号:%d\n年龄:%d\n地址:%s\n=================================\n", i + 1, stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
	}

	//存储到二进制文件中
	void save(struct Student_type* stud);
	save(stud);

	//再从二进制文件读取数据,并打印
	struct Student_type forRead[10];
	void read(struct Student_type* forRead);
	read(forRead);
}

void SelectSort(struct Student_type* stud) {
	for (int i = 0, k; i < SIZE - 1; i++) {
		k = i;
		for (int j = i + 1; j <= SIZE - 1; j++) {
			if (/*(*(stud + j)).num > (*(stud + k)).num*/stud[j].num > stud[k].num) {
				k = j;
			}
		}
		if (stud[k].num != stud[i].num) {
			struct Student_type temp;
			temp = stud[k];
			stud[k] = stud[i];
			stud[i] = temp;
		}
	}
	return 0;
}

void BubbleSort(struct Student_type* stud) {
	struct Student_type temp;
	for (int i = 0; i <= SIZE - 2; i++) {//冒泡SIZE-1次
		for (int j = 0; j <= SIZE - 2 - i; j++) {//每趟从0开始一直比到已确定项的前两项(把这一项与后一项比较,所以不是到已确定项的前一项)
			if (stud[j].num < stud[j + 1].num) {
				temp = stud[j];
				stud[j] = stud[j + 1];
				stud[j + 1] = temp;
			}
		}
	}
	return 0;
}

void save(struct Student_type* stud) {
	FILE* fp;
	if ((fp = fopen("C:\\Users\\WangTianpei\\Desktop\\stu.dat", "wb")) == NULL) {
		printf("\n创建失败!\n");
		exit(0);
	}
	for (int i = 0; i <= SIZE - 1; i++) {//二进制文件大小为360Byte,每个结构体占36Byte
		if (fwrite(&stud[i], sizeof(struct Student_type), 1, fp) != 1) {
			printf("\n存储失败!\n");
			exit(0);
		}
	}
	fclose(fp);
	return 0;
}

void read(struct Student_type* forRead) {
	FILE* fp = fopen("C:\\Users\\WangTianpei\\Desktop\\stu.dat", "rb");
	if (fp == NULL) {
		printf("\n打开文件失败!\n");
		exit(0);
	}
	printf("读取二进制文件结果为:\n=================================\n");
	for (int i = 0; i <= SIZE - 1; i++) {
		if (fread(&forRead[i], sizeof(forRead[i]), 1, fp) != 1) {
			printf("\n文件读取失败!\n");
			exit(0);
		}
		printf("序号:%d:\n姓名:%s\n学号:%d\n年龄:%d\n地址:%s\n=================================\n", i + 1, forRead[i].name, forRead[i].num, forRead[i].age, forRead[i].addr);
	}
	fclose(fp);
	return 0;
}

void _18_随机访问文件时修改游标位置的几个函数() {
	FILE* fp = fopen("new file.txt", "w");
	long where = ftell(fp);
	printf("where = %ld\n", where);
	fputc('A', fp);
	where = ftell(fp);
	printf("where = %ld\n", where);
	fputs(" boy", fp);
	where = ftell(fp);
	printf("where = %ld\n", where);
	fseek(fp, -1L, 1);
	where = ftell(fp);
	printf("where = %ld\n", where);
	rewind(fp);
	where = ftell(fp);
	printf("where = %ld\n", where);
	fclose(fp);
}

void _19_() {
	//10.4在桌面建立两个文本文件test1.txt和test2.txt,各存放一行字母(大写小写加空格),现要求把这两个文件中的信息合并(按照字母顺序),输出到一个新文件C中去

	//思路是先后读入两个字符串,拼接在同一个字符数组中,然后再一次性对整个字符数组排序,再输出到文件(这种比较简单容易实现)
	//另一种思路是将两个字符串分别读到两个字符数组中,各自排序,再将两个有序的字符数组合并为一个,再输出到文件

	FILE* fp = fopen("C:\\Users\\WangTianpei\\Desktop\\test1.txt", "r");
	if (fp == NULL) {
		printf("打开test1.txt失败!\n");
		exit(0);
	}
	printf("test1.txt:\n");
	char ch;
	char str[60] = "";
	int i;
	for (i = 0; (ch = fgetc(fp)) != EOF; i++) {
		str[i] = ch;
		putchar(str[i]);
	}
	fclose(fp);
	if ((fp = fopen("C:\\Users\\WangTianpei\\Desktop\\test2.txt", "r")) == NULL) {
		printf("打开test2.txt失败!\n");
		exit(0);
	}
	printf("\ntest2.txt:\n");
	for (; (ch = fgetc(fp)) != EOF; i++) {
		str[i] = ch;
		putchar(str[i]);
	}
	fclose(fp);
	putchar('\n');
	//采用冒泡排序
	int temp;
	for (i = 1; i <= strlen(str) - 1; i++) {
		for (int j = 0; j <= strlen(str) - 1 - i; j++) {
			if (str[j] > str[j + 1]) {
				temp = str[j];
				str[j] = str[j + 1];
				str[j + 1] = temp;
			}
		}
	}
	printf("test3.txt:\n");
	fp = fopen("C:\\Users\\WangTianpei\\Desktop\\test3.txt", "w");
	if (fp == NULL) {
		printf("打开text3.txt失败!\n");
		exit(0);
	}
	puts(str);
	fputs(str, fp);
	fclose(fp);
}

void _19_另一种实现() {
	//不如前面那种,但是一种思路,涉及合并两个已经有序的数组
	/*
	文件中写入
	 china(换行)
	 india
	在保存即可运行
	*/
	char str1[20], str2[20];
	//从文件读入
	FILE* fp = fopen("C:\\Users\\WangTianpei\\Desktop\\test1.txt", "rb+");
	fscanf(fp, "%s%s", str1, str2);
	int youbiao1 = ftell(fp);
	printf("共读取到#%d#个字符\n", youbiao1);

	/*手动键盘输入
	printf("请输入两个大小写字母组成的字符串:");
	scanf("%s%s", str1, str2);
	*/

	//一个冒泡
	for (int i = 0, temp; i < strlen(str1) - 1; i++) {
		for (int j = 0; j < strlen(str1) - 1 - i; j++) {
			if (str1[j] > str1[j + 1]) {
				temp = str1[j];
				str1[j] = str1[j + 1];
				str1[j + 1] = temp;
			}
		}
	}
	//一个选择
	for (int i = 0, k, temp; i < strlen(str2) - 1; i++) {
		k = i;
		for (int j = i + 1; j < strlen(str2); j++) {
			if (str2[j] < str2[k]) {
				k = j;
			}
		}
		if (k != i) {
			temp = str2[i];
			str2[i] = str2[k];
			str2[k] = temp;
		}
	}
	printf("各自排序后的字符串为:\n%s\n%s\n", str1, str2);
	//合并两个升序的字符数组
	char str3[40] = "";
	int i = 0, j = 0, k = 0;
	//两个字符数组都没有遍历完时,从前往后挨个比较两个字符数组,谁小谁进str3
	while (i <= strlen(str1) - 1 && j <= strlen(str2) - 1) {
		if (str1[i] <= str2[j]) {
			str3[k] = str1[i];
			i++; k++;
		}
		else {
			str3[k] = str2[j];
			j++; k++;
		}
	}
	//其中一个遍历完了,直接把另一个剩下的全部接到str3后面
	if (str1[i] == '\0') {
		while (str2[j] != '\0')
			str3[k++] = str2[j++];
	}
	else {
		while (str1[i] != '\0')
			str3[k++] = str1[i++];
	}
	str3[k] = '\0';
	printf("合并后的字符串为:\n%s\n", str3);

	//输出到同一个文件末尾
	fputc('\n', fp);
	fwrite(str3, strlen(str3), 1, fp);
	printf("共输出#%d#个字节\n", ftell(fp) - youbiao1);
	fclose(fp);
}

void _20_从旧文件读20个整数再由小到大排序然后输出到新文件(int argc, char* argv[]) {
	/*假设一个文本文件中保存着20个整数,请将这20个整数从小到大的顺序写到另一文件中去,并且在新文件中每4个整数占一行.源文件名和目标文件名通过命令行参数获取*/
	int a[20];
	if (argc != 3) {
		printf("未输入源文件名和目标文件名!");
		exit(0);
	}
	//从源文件读入
	FILE* fpin = fopen(argv[1], "r");
	FILE* fpout = fopen(argv[2], "w");
	if (fpin == NULL || fpout == NULL) {
		printf("文件打开失败!");
		exit(0);
	}
	for (int i = 0; i < 20; i++) {
		fscanf(fpin, "%d", &a[i]);
	}
	//冒泡排序
	int temp;
	for (int i = 0; i < 19; i++) {
		for (int j = 0; j < 19 - i; j++) {
			if (a[j] > a[j + 1]) {
				temp = a[j + 1];
				a[j + 1] = a[j];
				a[j] = temp;
			}
		}
	}
	//存入新文件
	for (int i = 0; i < 20; i++) {
		/*if ((i + 1) % 4 == 0) {
			fprintf(fpout, "%d\n", a[i]);
		}
		else {
			fprintf(fpout, "%d ", a[i]);
		}*/
		fprintf(fpout, "%d ", a[i]);
		if ((i + 1) % 4 == 0 && i != 19)
			fputc('\n', fpout);
	}
	fclose(fpin);
	fclose(fpout);
	//有趣的题目.exe C:\\Users\\WangTianpei\\Desktop\\old.txt C:\\Users\\WangTianpei\\Desktop\\new.txt
}


void _21_文件读写相关函数总结/*不可运行*/() {
	FILE* fp = fopen("filename", "wb");//FILE是stdio.h中定义的文件结构体,一个文件结构体代表一个文件
/*
fopen返回文件结构体的地址,filename是文件名,可以是字符数组名,字符指针,字符串常量,不写完整地址就是当前源文件所在目录,完整地址写法如下,如桌面名为test1.txt的文件:"C:\\Users\\WangTianpei\\Desktop\\test1.txt"注意是转义字符双反斜杠
*/
/*
文件打开方式字符串:
rb:打开一个已存在的二进制文件,游标位于0字节,从文件读
wb:如果打开已存在的二进制文件,会先删除再重建,游标位于0字节,然后可以开始写;如果打开不存在的二进制文件,就会直接新建,游标位于0字节,然后开始写;
ab:打开一个已存在的二进制文件,向末尾追加写数据,写完游标位于最后一个字节之后
r,w,a:与带b区别就是打开/创建的都是文本文件(ASCII码文件)
*/
/*
b的意思是二进制输入输出,实际上区别就是:
不带b认为是输出文本文件'\n'会转成'\r''\n' 或 入读文本文件'\r''\n'会转成'\n',而带b的输入输出时'\n'与'\r''\n'不会互相转换
原因是windows中,文本用'\r''\n'表示换行,而C语言中只需要'\n'
真正决定是二进制读写还是文本读写的是读写函数,b只控制转不转'\n'
*/
/*
实际考试中,例如:
要入读已存在的文本文件,当中有换行字符,应当使用r,如果用rb,读入字符数组后就会多一个'\r';
当要求把运算结果输出成文本文件时,应当使用w,输出成二进制文件则应当使用wb,如果没有要求就用wb即可;
如果要从一个文件中读,数据在内存中处理完毕,写入另一个文件,两个文件都创建文件指针,一个用r/rb,另一个用w/wb
*/

/*stdin,stdout是程序运行时自动打开的文件,代表键盘和屏幕*/
	int i;
	fscanf(stdin, "%d", &i);//==scanf("%d", &i);
	fprintf(stdout, "%d\n", i);//==printf("%d\n", i);

	fclose(fp);//关闭文件指针,fp不再指向原文件结构体,之后fp可以指向其他文件结构体


	FILE* in = fopen("filename", "rb");
	FILE* out = fopen("filename", "wb");

	//下面是按照文本文件方式读写函数,即把内存中的变量转成字符、字符串输出,或者把文件中的字符、字符串读入转成变量,如:int i = 10;在内存中占4字节,但是输出到文件只有'1''0'两个字符,占2字节

	/*@@字符读写函数@@*/
	fgetc(in);//类似getchar(),用的是它的返回值,所以无需其他参数,只要一个文件指针,成功读取返回所读字符,可以读取任意字符包括'\n'、' '等,若文件读完了,会读取最后的文件结束标志EOF(ASCII码值为-1)

	//从头读文件到尾,用EOF作为循环结束条件
	char ch = fgetc(in);
	while (ch != EOF) {
		//while (!feof(in)) { 也可以这样写,feof函数的作用是判断文件结束标志EOF是否被读过了,如果就为真,否则为假,再取反就为真,意识当文件fp没有被读完时进入循环,与while (ch != EOF)效果完全一致
		putchar(ch);//或者其他操作
		ch = fgetc(in);
	}

	//等价于如下写法
	while ((ch = fgetc(in)) != EOF) {
		printf("%c", ch);
	}

	fputc(ch, out);//类似putchar(ch),返回值不重要,把字符ch写到文件out中
	fputc('\n', out);

	/*@@字符串读写函数@@*/
	char str1[10];
	int n = sizeof(str1);
	fgets(str1, n, in);//从文件in中读入一个长度为n-1的字符串,然后自动在末尾添加'\0',一共长n个字节,送入字符数组str中;类似于gets(str),多了一个长度参数和文件指针参数,fgets和gets一样可以读取' ',如果在读完n-1个字符之前遇到'\n'或者文件结束符EOF,读入就结束,但是会将遇到的'\n'也作为一个字符读入,这一点与gets很像,gets也会清除回车,但不会把回车读入;fgets执行成功返回str数组首元素地址(一般成功时的返回值不重要),如果刚开始读就遇到EOF就会返回空地址NULL,这可以用来作为文件读完的标志

	//从头读文件到尾,用返回值空地址NULL作为循环结束条件
	/*
	例如文件中写的是:
	China
	India
	Brazil
	*/
	char s[3][10];
	i = 0;
	while (fgets(s[i], 10, in) != NULL) {
		printf("%s", s[i]);//注意此处无需换行符'\n',因为fgets遇到'\n'就结束,但会将'\n'一并读入;或者其他操作
		i++;
	}

	fputs(str1, out);//把str所指的字符串写入文件out中,字符串末尾的'\0'不输出,第一个参数可以是字符串常量、字符数组名或是字符指针;fputs与puts类似,只是多了一个文件指针参数,puts会自动加'\n',而fputs不会往文件中加'\n'
	fputs("China", out);
	char* ptr = "America";
	fputs(ptr, out);

	/*@@格式化读写函数(整型、实型、字符、字符串皆可)@@*/
	int a;
	double b;
	fscanf(in, "%d%lf", &a, &b);//从文件in中按照指定的格式去读,把文件中的字符转成变量,例如文件中是"12 13.4",则a==12,b==13.4,返回值是成功读取的变量个数,文件结束了没读到返回的是EOF(==-1),这可以用来判断文件是否读完了;与scanf类似,只是前面增加了一个文件指针参数,读字符串会自动加'\0',与scanf一样%d%f%s读不了' ',遇到'\n'会舍弃

	//从头读文件到尾,用fscanf返回值作为循环结束条件
	while (fscanf(in, "%d", &a) != EOF) {//!=EOF或者!=-1表示读到了数据,当没有成功读到数据就等于EOF即-1;或者==1表示当返回值等于1意味着读到了数据就进入循环,没返回值不等于1意味着文件读完了循环结束
		printf("%d\n", a);//或者其他操作
	}

	fp = fopen("C:\\Users\\WangTianpei\\Desktop\\test1.txt", "rb");
	//test.txt文件内容ab cd ef *
	char str[4][10];
	i = 0;
	while (fscanf(fp, "%4s", str[i]) != -1) {//%s之间可以指定真实读取的字符串的长度,末尾自动加'\0',可以代替fgets(str,n,fp)读取指定长度字符串,但遗憾是不能读取空格' '
		printf("%s|", str[i++]);//运行结果ab|cd|ef|*|
	}
	/*while (fgets(str[i], 4, fp) != NULL) {
		printf("%s|", str[i++]);//运行结果ab |cd |ef |*|
	}*/
	fclose(fp);


	fprintf(out, "%d,%-6.2lf\n", a, b);//向文件out中按照指定格式写入,把内存中的变量转成字符串输出,例如int a = 10;内存中4字节但是转成字符串只有'1''0'两个字节;与printf类似,前面多一个文件指针参数


	//下面是按照二进制文件方式读写函数,即把内存中的变量所占字节中实际的内容输出,或者把文件中指定字节数的内容读入,内存与文件中的字节内容完全一致,如:int i = 10;在内存中占4字节,输出到文件也是4字节,如果记事本打开是乱码,因为每个字节并不是可以显示的ASCII码

	/*@@二进制字节块读写函数(一般用于数组、结构体读写)@@*/
	/*
	fread(buffer, size, count, fp);
	fwrite(buffer, size, count, fp);
	直接将内存中指定地址开始的指定个数、指定长的一块内存空间输出到文件,或者从文件读入内存,每个字节是什么就读写什么,变量不会转换成字符形式,是二进制的读写函数
	buffer:地址,读就是用于存放数据的变量的起始地址,写就是要输出的变量的起始地址
	size:每次读写的字节数,即每个变量的大小
	count:读写的次数,即读写变量的个数
	fp:文件指针,即读写的文件
	fread fwrite返回值是成功读写的个数,也就是count的值;当最后一次读取字节不满时或者直接遇到EOF时,都不算成功读取,返回0,这可以作为文件读完的标志,尽管不算成功读取,但是会把不满size的字节读取,强行赋值给buffer开始的地址
	*/
	typedef struct stu {
		int a;
		char b;
		float c[2];
	}stu;
	stu shuzu[3] = { {1,'a',1.1,1.2},{2,'b',1.3,1.4},{3,'c',1.5,1.6} };
	fread(shuzu, sizeof(stu), 3, in);
	//也可以自己写循环来控制读取个数
	for (int i = 0; i < 3; i++) {
		fread(&shuzu[i], sizeof(stu), 1, in);
	}
	//当不知道文件中有多少个结构体时,应当用返回值来作为循环结束条件
	for (int i = 0; fread(shuzu + i, sizeof(stu), 1, in) != 0; i++) {
		printf("%d,%c,%f,%f\n", shuzu[i].a, shuzu[i].b, shuzu[i].c[0], shuzu[i].c[1]);
	}

	fwrite(shuzu, sizeof(stu), 3, out);
	//也可以自己写循环来控制写出个数
	for (int i = 0; i < 3; i++) {
		fwrite(shuzu + i, sizeof(stu), 1, out);
	}

	/*注意:同一个文件只有一个文件缓冲区,故不能同时读写,必须关闭其中一个,才能用另一个
	FILE* in = fopen("file1","rb");
	FILE* out = fopen("file1","wb");
	fwrite(buffer,size,count,out);
	fclose(out);//如果这句与下一句交换,会导致读不了数据
	fread(buffer,size,count,in);
	fclose(in);
	所以最好打开一个文件读或写,用完了就关闭,再读写再打开
	*/


	/*
	4个读取函数,判断文件是否读完,各自的循环判断条件总结:
	while(fgetc(in) != EOF)表示还没有读完,因为文件后有文件结束标志,算一个字符,ascii码为-1
	while(fgets(str,n,in) != NULL)表示还没有读完,因为fgets成功返回值是字符数字str的地址,失败返回NULL
	while(fscanf(in,"%d",&a) != -1)表示还没有读完,因为fscanf返回值是成功读取的个数,文件结束没有读到时返回EOF==-1
	int i = 0;
	while(fread(数组 + i,sizeof(元素类型),1,in) != 0){
		...
		i++;
	}表示还没有读完,因为返回值是成功读取的个数,也就是count的值,当最后一次读取字节不满时或者直接遇到EOF时,都不算成功读取,返回0
	*/


	//最后是与改变游标位置以便随机访问(或是重新读取)的相关函数
	//游标始终指向下一个要读写的字节,如果rb打开,那么一开始是0字节,最后读完了是文件最后一个字节后一个字节

	feof(in);//用于判断EOF是否被读过,如果是,返回值为真1,如果没有,返回值为假0,通常与fgetc连用
	ch = fgetc(in);
	if (!feof(in)) {
		//对ch操作
		ch = fgetc(in);
	}

	rewind(in);//重绕,倒带;使得游标回到0字节,例如:当以rb方式打开文件,读取完毕后,游标指向文件最后一个字节的后一个字节,这时候想要重新读取,可以rewind(in);或者fclose(in);后重新in=fopen("filename","rb");

	//fseek(文件指针,偏移量,偏移基点);
	//偏移量正表示往文件尾移动游标,偏移量负表示往文件头移动游标,这个形参是long型的,常量后面要加L
	//偏移基点是偏移的起始点,有3个值可以取,0表示文件开始位置0号字节,1表示当前位置,也就是即将读写的字节,2表示文件末尾位置,是文件最后一个字节后一个字节
	fseek(in, 0L, 1);//表示文件in的游标从当前位置往后偏移0字节,原地不动
	fseek(in, 10L, 0);//表示文件in的游标从文件开头位置往后偏移10字节
	fseek(out, -1L, 2);//表示文件out的游标从文件末尾位置往前偏移1字节
	fseek(out, -ftell(out), 2);//意思是将文件out的游标从末尾往前偏移当前游标所指向的字节数个字节,也就是游标指向了0字节,ftell刚好返回long型的,符合fseek第二个形参类型,-ftell(out)表示对返回值取负,'-负号'是单目运算符;等价于rewind(out);如果此时继续往文件写,会逐个覆盖掉原来文件中的字节

	ftell(out);//返回当前游标指向的字节数,返回值是long型的,通过ftell可以知道已经读写了多少个字节

	fclose(in);
	fclose(out);
}


void _22_枚举类型总结() {
	typedef enum color {
		red, yellow, blue = 9, white, black
	} color;//enum color为枚举类型只能取到几个枚举常量,重命名为color
	//枚举常量是常量,在定义枚举类型大括号外不可以对red,yellow...重赋值
	//每一个枚举常量代表一个整数,默认是0,1,2,3,4...,例如red=0,yellow=1,也可以定义枚举类型时人为指定,如blue=9,之后的枚举常量依次+1,如white=10,black=11
	enum color a;//a叫枚举变量
	color b;//b也是
	a = red, b = black;//逗号表达式优先级最低,低于赋值运算符,自左向右结合,双目,结果为右值;赋值运算符以及条件运算符,单目运算符,自右向左结合,赋值运算符结果为左值
	//a=red相当于a=0   ,b=black相当于b=11;
	printf("a=%d,b=%d\n", a, b);//a=0,b=11,C99把枚举类型当做整型数据的一种,即用户自定义的整型类型,因而枚举变量可以像整型一样输出
	a = 12;//虽然超过了最大枚举常量的值,但并不会报错,不过没有实际意义
	b = 9;
	printf("a=%d,b=%d\n", a, b);//a=12,b=9

	enum { aa = 9, bb = 0, cc, dd } c, d = aa, e;//可以声明匿名枚举类型,c,d,e是该类型的枚举变量
	c = cc;//枚举常量如果也叫c,这里c=c会报错,重名了
	printf("c=%d,d=%d\n", c, d);//c=1,d=9

	//枚举常量,枚举变量,整型可以相互比较大小
	if (d == aa) {//枚举变量与枚举常量比较
		printf("yes\n");//yes
	}
	if (d > 8) {//枚举变量与整型比较
		printf("yes\n");//yes
	}
	if (d >= c) {//枚举变量与枚举变量比较
		printf("yes\n");//yes
	}
	if (aa == 9) {//枚举常量与整型比较
		printf("yes\n");//yes
	}
	if (cc > aa) {//枚举常量与枚举常量比较
		printf("yes\n");
	}
	else {
		printf("no\n");//no
	}


}

void _23_数学函数的使用() {
	//数学函数的使用 #include<math.h>
	printf("|-5|=%d\n", abs(-5));//absolute value,求int型x的绝对值,返回int
	//π≈3.14159265358979323846
	printf("cos(π/3)=%.1lf\n", cos(M_PI / 3));//求cos(x),x为double,返回double
	printf("e^2=%lf\n", exp(2));//求e^x,x为double,返回double,e≈2.718
	printf("ln(e)=%.1lf\n", log(M_E));//求ln(x),x为double,返回double
	printf("e^ln(1)=%.1lf\n", exp(log(1)));
	printf("|-0.3|=%.1lf\n", fabs(-0.3));//float absolute value,求double型x的绝对值,返回double
	printf("|3|=%.1lf\n", fabs(3));
	printf("[2.4]=%.1lf\n", floor(2.4));//取整函数[x],x为double,返回double
	printf("%.1lf\n", (double)(int)2.4);//相当于先转int型再转double型
	printf("log10(100)=%.1lf\n", log10(100));//求log10(x),x为double,返回double
	printf("2^3=%.1lf\n", pow(2, 3));//power幂,乘方,求x^y,xy均为double,返回double
	printf("rand()=%d\n", rand());//产生-90~32767的随机整数,无需参数,返回int
	printf("sin(3π/2)=%.1lf\n", sin(M_PI * 3 / 2));//求sin(x),x为double,返回double
	printf("根号2=%.3lf\n", sqrt(2));//square root,求根号x,x为double,返回double
	printf("2^0.5=%.3lf\n", pow(2, 1.0 / 2));//相当于sqrt(2)
	printf("3^(1.0/3)=%.3lf\n", pow(3, 1.0 / 3));
	printf("根号3=%.3lf\n", sqrt(3));
	printf("根号5=%.3lf\n", sqrt(5));
	printf("tan(π/4)=%.1lf\n", tan(M_PI / 4));//求tan(x),x为double,返回double
	/*
	|-5|=5
	cos(π/3)=0.5
	e^2=7.389056
	ln(e)=1.0
	e^ln(1)=1.0
	|-0.3|=0.3
	|3|=3.0
	[2.4]=2.0
	2.0
	log10(100)=2.0
	2^3=8.0
	rand()=41
	sin(3π/2)=-1.0
	根号2=1.414
	2^0.5=1.414
	3^(1.0/3)=1.442
	根号3=1.732
	根号5=2.236
	tan(π/4)=1.0
	*/
}

void _24_全局变量_局部变量_动态存储_静态存储_内部函数_外部函数总结() {
	//全局变量与局部变量(从可见性(作用域)区分变量)
	/*
	局部变量只在,局部可见;全局变量从定义到本源文件结束可见
	局部变量:函数内部定义的、函数形参、程序块(复合语句)中定义的
	全局变量(外部变量):函数以外定义的[一般大写表示]
	全局变量会被同名的局部变量屏蔽
	*/

	//动态存储方式与静态存储方式(从生存期区分变量)
	/*
	全局变量全部是静态存储方式,静态存储方式在程序开始执行时就分配空间,程序执行完毕才释放空间,在编译时就赋初值,只能用常量初始化;
	没有用static声明的局部变量(自动变量)采用动态存储方式,动态存储方式在函数调用开始分配存储空间,函数调用结束释放空间,多次调用同一函数,就会多次分配释放存储空间,可能不是同一地址

	☀每一个变量和函数都有两个属性:数据类型和存储类别;
	数据类型指整型、浮点型...,存储类别指数据在内存中存储的方式,如动态存储、静态存储
	在定义变量、函数时应同时指定数据类型和存储类别,如果不指定存储类别,则系统隐含默认指定为一种存储类别
	C语言包含4种存储类别:自动的auto、静态的static、寄存器的register、外部的extern

	局部变量的存储类别:
	1.自动变量(动态局部变量):局部变量(函数内部定义的、函数形参、程序块(复合语句)中定义的)如果前面没有static声明,都是动态存储方式,调用函数时赋初值,系统自动分配空间,调用结束自动释放空间,故称为"自动变量",在定义前面加auto表示自动变量auto int b,c=3;//定义b,c为自动变量

	☀auto可以省略不写,因为默认就是auto自动存储类别
	☀总结:不加static声明的局部变量采用动态存储方式,是动态局部变量,即自动变量

	2.静态局部变量(前面加static声明的局部变量)
	如果希望函数中的局部变量在函数调用结束之后不释放,下一次调用函数时,继续使用上一次调用结束时的值,就可以在定义局部变量时前面加static,成为静态局部变量.
	静态局部变量采用静态存储方式,在编译时赋初值,只能用常量初始化,在整个程序运行期间都不释放空间,程序运行时已经有初值,但可见性依然只有函数内部

	☀对于静态局部变量,如果没有指定初值,编译时自动赋值0(对数值型变量)或空字符'\0'(对字符变量);而对于自动变量(动态局部变量),它的值是不确定的
	☀函数的形参无法用static指定为静态局部变量

	3.寄存器变量
	也是动态存储方式,了解即可,register int f;//将局部变量的值不放在内存中而是放在CPU的寄存器中,以提高执行效率
	☀3种局部变量存储位置不同,自动变量(动态局部变量)存储在动态存储区,静态局部变量存储在静态存储区,寄存器变量存储在CPU的寄存器中

	全局变量(外部变量)的存储类别:
	全局变量(外部变量)都是采用静态存储方式的,生存期是整个程序运行过程中(即全程存在),
	可见性是从定义处开始,一直到程序末尾,在编译时就赋初值,只能用常量初始化

	使用关键字extern扩展外部变量的可见性:
	全局变量(外部变量)可以扩展可见性,可以用关键字extern对该外部变量作"外部变量声明",表示把该外部变量的可见性扩展到定义之前的某个位置,使得从"外部变量声明"处开始该外部变量可见
	...
	void main(){
	extern int A,B,C;//外部变量声明后,从声明开始A,B,C就可见
	...
	}
	int A,B,C://原来只有定义处之后全局变量A,B,C才可见
	...
	☀用extern扩展外部变量可见性时,类型名可以省略,如extern int A,B,C == extern A,B,C,因为不是定义,所以可以不写类型,只写外部变量名即可
	另外,extern不仅可以在本文件中扩展外部变量可见性,甚至还可以将本文件的外部变量可见性扩展到另一个文件中

	使用关键字static将外部变量可见性限制在本文件中:
	外部变量(全局变量)本来就是静态存储的,定义时前面再加个static修饰,意思是将可进行现在本文件中,其他文件没法使用extern扩展可见性(似乎在本文件中也没法用extern扩展可进行了)
	file1.c
	static int A;
	file2.c
	extern A;//企图扩展可见性无效

	☀关键字static总结:
	(1)对局部变量用static声明,表示由动态存储方式变为静态存储方式,在编译时就分配空间赋初值,在整个程序执行期间不释放空间
	(2)对全局变量(外部变量)用static声明,表示将该变量局部化,将可见性限制在本文件中,其他文件无法用extern扩展
	*/

	//内部函数和外部函数
	/*
	与全局变量(外部变量)可以在本文件使用,也可以extern扩展到其他文件类似,根据函数能否被其他源文件调用,将函数区分为"内部函数"和"外部函数"

	内部函数:
	在定义函数时,在函数返回值类型前加上static修饰,它就成了内部函数,这与static修饰全局变量类似,使得函数只能被本文件的其他函数调用
	static int fun(int a,int b);
	因为前面有static,所以内部函数又叫静态函数
	通常把只能由本文件使用的函数和外部变量(全局变量)放在文件开头,前面都冠以static使之局部化,其他文件不能引用

	外部函数:
	在定义函数时,在函数返回值类型前面加上extern修饰,它就成了外部函数,可供其他文件调用,extern可以省略,默认就是外部函数
	extern int fun(int a,int b){//extern可以省略
		......
	}
	在其他文件中调用该函数时,需要先声明,函数声明可以加上extern也可以省略,这一点与全局变量(外部变量)扩展可见性到本文件上文/其他文件时用extern声明类似
	char str[30];
	extern void enter_string(char str[]);//extern可以省略
	enter_string(str);
	*/
}


int yinzihe(int i) {
	int sum = 1, j, temp;
	if (i % 2 == 0)
		temp = i / 2;
	else
		temp = i / 3;
	for (j = 2; j <= temp; j++) {
		if (i % j == 0)//说明j是i的因子
			sum += j;
	}
	return sum;
}
void _25_亲密数对() {
	//大纲样卷最后一题
	//(15分)求2-500之间的所有亲密数对并保存到文件data.txt中.亲密数对的定义为:如果M的因子(包括1,但不包括自身)之和为N,且N的因子之和为M,则称M与N为一对亲密数.



	//答案思路:
	//int m, n, k, i;
	// FILE *fp = fopen("data.txt","w");
	//for (m = 2; m <= 500; m++) {
	//	n = 1;
	//	for (i = 2; i <= m / 2; i++)
	//		if (m % i == 0)n += i;//n是m的因子和
	//	k = 1;
	//	for (i = 2; i <= n / 2; i++)
	//		if (n % i == 0)k += i;//k是n的因子和
	//	if (k == m && m <= n) {//n与m互为亲密数,且是第一次发现
	//		printf("m=%3d,n=%3d\n", m, n);
	//		fprintf(fp,"m=%3d,n=%3d\n", m, n);
	//	}
	//}
	//fclose(fp);

		/*运行结果:
		m = 6, n = 6
		m = 28, n = 28
		m = 220, n = 284
		m = 496, n = 496*/



		//自身可能是自身的亲密数,比如6;不可能有三个不同的数a,b,c互为亲密数,因为如果成立,那么b的因子和等于a也等于c,说明a=c,是同一个数,故矛盾
	int s[501] = { 0 };//只用2-500
	for (int i = 2, temp; i <= 500; i++) {
		if (s[i] == -1)continue;//说明i有亲密数,且那个数在它之前,无需重复计算
		//判断i在2-500有没有亲密数
		temp = yinzihe(i);
		if (temp >= 2 && temp <= 500) {
			//说明temp可能是i的亲密数
			if (yinzihe(temp) == i) {
				//说明temp与i互为亲密数
				s[i] = temp;
				if (i != temp)//如果自身不是自身的亲密数
					s[temp] = -1;//表示temp有亲密数,且那个数在它之前,最后输出时无需重复打印
			}
		}
	}


	//在命令行显示
	for (int i = 2; i <= 500; i++) {
		if (s[i] != 0 && s[i] != -1)
			printf("%d<--->%d\n", i, s[i]);
	}

	//输出到文本文件
	FILE* fp = fopen("data.txt", "w");
	for (int i = 2; i <= 500; i++) {
		if (s[i] != 0 && s[i] != -1)
			fprintf(fp, "%d<--->%d\n", i, s[i]);
	}
	fclose(fp);

	//运行结果:
	//6 <--->6
	//28 <--->28
	//220 <--->284
	//496 <--->496
}

_26_宏定义与复合赋值运算符() {
#define N 2
#define Y(n) N+n
	int z = 2 * Y(5);//2*2+5
	printf("z=%d\n", z);//z=9,宏定义是直接粘贴到调用处
	int a = 3, b = 4;
	a *= b + 1;//a=a*(b+1)
	printf("a=%d\n", a);//a=15,复合赋值运算符是先把右边算完再加减乘除
}

int main(int argc, char* argv[]) {
	//_1_或和与运算的短路现象();
	//_2_冒泡排序算法();
	//_3_选择排序算法();
	//_4_有序数组插入一个数_正序后移();
	//_5_汉诺塔();
	//_6_i加加和加加i运算顺序问题以及函数参数的运算顺序();
	//_7_最大公约数和最小公倍数();
	//_8_回文串();
	//_9_折半查找法();
	//_10_进制转换();
	//_11_递归法将int转成数字char数组();
	//_12_对用字符指针数组表达的字符串数组按字母顺序进行选择排序();
	//_13_动态建立链表();
	//_14_使用fgetc和fputc写文件a并复制到b();
	//_15_从键盘读入若干个字符串_对他们按字母大小顺序排序_然后把排好序的字符串送到磁盘文件中保存();
	// _15_改_根据指定的字符串个数n_动态建立了字符指针数组_并动态申请空间让每一个字符指针都指向一维字符数组_相当于二维数组的功能_但排序时只需要改变字符数组指向即可_无需真的交换字符数组中的内容_排序写了冒泡和选择两种_并且用fputs输出到文件file1_再用fgets读入();
	//_16_使用fprintf和fscanf输出输入数值到文件();
	//_17_从键盘输入10个学生的信息_并按照学号排序_再输出到二进制文件_最后从文件读取数据_并打印在屏幕上();
	//_18_随机访问文件时修改游标位置的几个函数();
	//_19_();//10.4在桌面建立两个文本文件test1.txt和test2.txt,各存放一行字母(大写小写加空格),现要求把这两个文件中的信息合并(按照字母顺序),输出到一个新文件C中去
	//_19_另一种实现();
	//_20_从旧文件读20个整数再由小到大排序然后输出到新文件(argc, argv);
	//_21_文件读写相关函数总结/*不可运行*/()
	//_22_枚举类型总结();
	//_23_数学函数的使用();
	//_24_全局变量、局部变量、动态存储、静态存储、内部函数、外部函数总结();
	//_25_亲密数对();
	//_26_宏定义与复合赋值运算符();



	/*见notability
	int main(int argc, char* argv[]) {
		for (int i = 0; i < argc; i++) {
			printf("argument %d is %s\n", i, argv[i]);
		}
		printf("*(argv+2)=%s\n", *(argv + 2));
		printf("*(*(argv+2))=%c\n", *(*(argv + 2)));
	}*/

}